classdef FluxGate < handle
    %FLUXGATE Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
        port
        dev
        
        waiting_time
        max_read_num
    end
    
    methods
        function obj = FluxGate(port)
            %FLUXGATE Construct a FluxGate object
            %   Use 'read_data' and 'read_single_data' to obtain magnetic
            %   field value(s).
            warning('off','MATLAB:serial:fscanf:unsuccessfulRead');
            
            obj.port = port;
            obj.waiting_time = 1;
            obj.max_read_num = 100;
            
            obj.dev = instrfind('Type', 'serial', 'Port', port, 'Tag', '');

            if isempty(obj.dev)
                obj.dev = serial(port);
            else
                fclose(obj.dev);
                obj.dev = obj.dev(1);
            end

            set(obj.dev, 'BaudRate', 19200);
            set(obj.dev, 'Timeout', 1.0);
            set(obj.dev, 'Terminator', {'LF/CR','LF/CR'});
            set(obj.dev, 'InputBufferSize', 8);

            fopen(obj.dev);
        end
        
        function delete(obj)
            warning('on','MATLAB:serial:fscanf:unsuccessfulRead');
            fclose(obj.dev);
        end
        
        %% basic operation
        function k = clear_buffer(obj)
            %CLEAR_BUFFER Clears the buffer by contiuously polling data.
            obj.stop();
            
            s = obj.readStr(); k = 0;
            while ~isempty(s)
                s = obj.readStr();
                k = k + 1;
            end
            fprintf('%d data points cleared.\n\n', k);
        end
        
        function data = read_single_data(obj)
            obj.start();            
            s = ''; k = 0;
            while isempty(s)
                s = obj.readStr();
                k = k + 1;
                if k > obj.max_read_num
                    error('No data read after %d test readout.', obj.max_read_num);
                end
            end
            obj.stop();
            data = str2double(s);
        end
        
        function data = read_data(obj, num)
            data = zeros(1,num);
            for k = 1:num
                pause(obj.waiting_time);
                data(k) = obj.read_single_data();
                fprintf('B(%d) = %5.1f\n', k, data(k));
            end
        end
        
        % If someone use Ctrl+C to interupt read_single_data() method, the
        % obj.stop() command may not be sent. In that case, there will be a
        % lot of old data points in the buffer. Call clear_buffer() method
        % before fetching data again can ensure you getting the up-to-date
        % measurement data.
        function data = robust_read_data(obj, num)
            obj.clear_buffer();
            data = obj.read_data(num);
        end
        
        %% Applications
        function [data, pos] = line_measurement(obj, x, titleStr)
            %Input 'x' is the coordinate array.
            
            if nargin == 2
                titleStr = 'Line Measurement';
            end
            fprintf('############################################################\nPerforming measurement: %s.\n############################################################\n', titleStr);
            
            data = zeros(size(x));
            pos = x;
            
            for k = 1:length(data)
                input(sprintf('Position = %f (Press ENTER to proceed)...', x(k)) );
                data(k) = obj.read_single_data();
                fprintf( 'Magnetic field B=%5.1f nT.\n\n', data(k));
                pause(obj.waiting_time);
            end
            
            %plot result
            neg_idx = find(data<=0);
            pos_idx = find(data>0);

            figure;
            semilogy( pos(neg_idx), -data(neg_idx), 'r*', ...
                      pos(pos_idx),  data(pos_idx), 'b+', ...
                      pos, abs(data), 'k-');
            title(titleStr);
            grid on;
        end
    end
    
    %%
    methods(Access = private)
        
        % If you try to run the following code, then obj.dev
        % may be closed by the delete() method, and you will get error:
        %   "OBJ must be connected to the hardware with FOPEN."
        %
        %     >> fg = EquipmentControl.FluxGate('COM3');
        %     >> fg = EquipmentControl.FluxGate('COM3');
        %     >> fg.read_data(1);
        %
        % So, remember to "clear fg" before you run the second line above.
        function s = readStr(obj)
            s = fscanf(obj.dev);
        end
        
        function obj = start(obj)
            fwrite(obj.dev, 's');
        end
        
         function obj = stop(obj)
            fwrite(obj.dev, 'q');
        end       
    end
end

